package util

import (
	"bufio"
	"errors"
	"fmt"
	"gitee.com/kristas/booting-go/framework/common/util/lang"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

func Error(msg string, args ...interface{}) {
	fmt.Printf("err: "+msg, args...)
	os.Exit(1)
}

func MkDir(dir string) error {
	if FileExist(dir) {
		return nil
	}
	if err := os.Mkdir(dir, 0754); err != nil {
		return err
	}
	return nil
}

func FileExist(file string) bool {
	_, err := os.Stat(file)
	if os.IsNotExist(err) {
		return false
	}
	return true
}

func GetPkgName() (string, error) {
	goModFile, err := FindGoModFile()
	if err != nil {
		return "", err
	}
	file, err := ioutil.ReadFile(goModFile)
	if err != nil {
		return "", err
	}
	err = errors.New("go.mod file error")
	arr := strings.SplitN(string(file), "\n", 2)
	if len(arr) < 1 {
		return "", err
	}
	line := lang.NewString(arr[0])
	if !line.StartWith("module") {
		return "", err
	}
	pkg := line.RemovePrefix("module").Trims().String()
	return pkg, nil
}

func FindGoModFile() (string, error) {
	wd, err := os.Getwd()
	defer os.Chdir(wd)
	if err != nil {
		return "", err
	}
	goModName := filepath.Join(wd, "go.mod")
	if !FileExist(goModName) {
		err := os.Chdir("../")
		if err != nil {
			return "", err
		}
		return FindGoModFile()
	} else {
		return goModName, err
	}
}

func PipeOutput(cmd *exec.Cmd, pipe func(line []byte)) error {
	out, _ := cmd.StdoutPipe()
	defer out.Close()
	cmd.Stderr = cmd.Stdout
	if err := cmd.Start(); err != nil {
		panic(err)
	}
	reader := bufio.NewReader(out)
	for {
		line, _, err := reader.ReadLine()
		if err != nil {
			if err.Error() != "EOF" {
				return err
			}
			break
		}
		pipe(line)
	}
	return nil
}

func TemporaryEntry(dir string) (back func() error, err error) {
	wd, _ := os.Getwd()
	err = os.Chdir(dir)
	if err != nil {
		return
	}
	back = func() error {
		return os.Chdir(wd)
	}
	return
}

type OpenFileHandler = func(mode int) (*os.File, error)

func ReplaceFileContents(oldString, newString string, n int, openFile OpenFileHandler) error {
	data, err := ReadFile(openFile)
	wrFile, err := openFile(os.O_RDWR | os.O_TRUNC)
	if err != nil {
		return err
	}
	defer wrFile.Close()
	contents := lang.NewString(string(data))
	// n = -1 replace all
	contents = contents.Replace(oldString, newString, n)
	_, err = wrFile.WriteString(contents.String())
	if err != nil {
		return err
	}
	return nil
}

func IsFileContains(sub string, openFile OpenFileHandler) (bool, error) {
	data, err := ReadFile(openFile)
	if err != nil {
		return false, err
	}
	if strings.Contains(string(data), sub) {
		return true, nil
	}
	return false, nil
}

func ReadFile(openFile OpenFileHandler) ([]byte, error) {
	file, err := openFile(os.O_RDONLY)
	if err != nil {
		return nil, err
	}
	data, err := io.ReadAll(file)
	if err != nil {
		return nil, err
	}
	file.Close()
	return data, nil
}

func OpenOrCreateFile(path string, mode int) (file *os.File, exists bool, err error) {
	if FileExist(path) {
		exists = true
		file, err = OpenFile(path, mode)
		return
	}
	file, err = OpenFile(path, mode|os.O_CREATE)
	return
}

func OpenFile(path string, mode int) (*os.File, error) {
	return os.OpenFile(path, mode, 0660)
}
